package core;

import gitobject.*;
import repository.Repository;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;

import fileoperation.FileCreation;
import fileoperation.FileReader;

public class JitReset {
	/**
	 * Add files into your index.
	 * @param mode: mode for reset, includes --soft, --mixed, --hard
	 * @param commitInfo: args of HEAD reset
	 * @throws IOException
	 */
	@SuppressWarnings("unused")
	public static void reset(String mode, String commitInfo) throws IOException {
		/* Todo: You should pass the filename in this function, and generate a hash file in your repository.
		 *   Add your code here.*/

		if(commitInfo.length() >= 4){
			boolean commitBefore = false;
			String startCommitId = Commit.getLastCommit();
			//judge input args commitInfo
			if(commitInfo.substring(0, 4).toUpperCase().equals("HEAD")){
				int loopTimes = countResetTimes(commitInfo.substring(4));
				commitBefore = true;
				resetToHead(startCommitId, loopTimes);
			}
			//use commitId to reset branch
			else if(commitInfo.length() == 40){
				Commit commit = Commit.deserialize(commitInfo);
				if(commit != null){
					commitBefore = containsCommitBefore(startCommitId, commitInfo, JitMap.index.getIndexMap(), false);
					commit.writeToHead();
				}
				else System.out.printf("fatal: Could not parse object '%s'.\n", commitInfo);
			}
			else System.out.printf("fatal: ambiguous argument '%s': unknown revision or path not in the working tree.\n", commitInfo);
			//reset --soft, reset head and recover index
			if(Objects.equals(mode, "--soft")){
				JitMap.setCommitMap(Commit.getLastCommit());
				if(commitBefore) {
					containsCommitBefore(startCommitId, commitInfo, JitMap.index.getIndexMap(), true);
				}
				else {
					JitMap.safeClearIndex();
				}
			}
			//reset --mixed, reset head and recover blobs
			else if(Objects.equals(mode, "--mixed")){
				String commitId = Commit.getLastCommit();
				JitMap.setCommitMapResetBlob(commitId, false);
				JitMap.safeClearIndex();
			}
			//reset --hard, reset head and recover blobs, delete files not included in repository.
			else if(Objects.equals(mode, "--hard")){
				String commitId = Commit.getLastCommit();
				JitMap.setCommitMapResetBlob(commitId, true);
				JitMap.safeClearIndex();
			}
			else System.out.println("");
			try {
				JitMap.index.compressWrite();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		else System.out.printf("fatal: ambiguous argument '%s': unknown revision or path not in the working tree.\n", commitInfo);
	}

	/**
	 * //count the total times of HEAD reset
	 * @param commitInfo args string of HEAD reset times.
	 * @return
	 */
	public static int countResetTimes(String commitInfo){
		int loopTimes = 0;
		int len = commitInfo.length();
		if(len == 0) return loopTimes;
		else if(commitInfo.charAt(0) == '^' || commitInfo.charAt(0) == '~') {
			loopTimes++;
			int numTimes = 0;
			boolean hasNum = false;
			for (int i = 1; i < len; i++) {
				if (commitInfo.charAt(i) == '^' || commitInfo.charAt(i) == '~') {
					loopTimes++;
					if(hasNum){
						loopTimes--;
						loopTimes += numTimes;
						numTimes = 0;
						hasNum = false;
					}
				}
				else if (commitInfo.charAt(i) >= '0' && commitInfo.charAt(i) >= '0'){
					numTimes = numTimes * 10 + (commitInfo.charAt(i) - '0');
					hasNum = true;
				}
				else{
					System.out.printf("fatal: ambiguous argument '%s': unknown revision or path not in the working tree.\n", commitInfo);
					break;
				}
			}
		}
		else{
			System.out.printf("fatal: ambiguous argument '%s': unknown revision or path not in the working tree.\n", commitInfo);
		}
		return loopTimes;
	}

	public static void resetToHead(String startCommitId, int loopTimes) throws IOException{
		String commitId = startCommitId;
		Commit commit = null;
		while(loopTimes != 0){
			if(Objects.equals(commitId, "" ) || commitId == null){
				System.out.println("warning: reset exceeds the maximum revision and is currently reset to uncommitted state");
				break;
			}
			else{
				commit = Commit.deserialize(commitId);
				assert commit != null;
				commitId = commit.getParent();
			}
			loopTimes--;
		}
		String finalCommitId = commitId;
		File branch = Commit.getNowHeadFile();
		if(branch == null){
			System.out.printf("Branch '%s' does not exist\r\n", JitBranch.getNowBranchName());
		}
		else{
			FileWriter fileWriter = new FileWriter(branch);
			if(Objects.equals(finalCommitId, "") || finalCommitId == null)
				fileWriter.write("");
			else fileWriter.write(finalCommitId);
			fileWriter.close();
		}
		JitMap.setCommitMap(finalCommitId);
		commitId = startCommitId;
		while(!Objects.equals(commitId, finalCommitId) && !Objects.equals(commitId, "" ) && commitId != null){
			commit = Commit.deserialize(commitId);
			assert commit != null;
			String treeId = commit.getTree();
			Tree commitTree = Tree.deserialize(treeId);
			assert commitTree != null;
			JitMap.setKeyForMap(JitMap.index.getIndexMap(), commitTree, ".", true);
			commitId = commit.getParent();
		}
	}

	public static boolean containsCommitBefore(String startCommitId, String commitInfo, Map<String, String> tempMap, boolean setMap) throws  IOException{
		String commitId = startCommitId;
		while(!Objects.equals(commitId, commitInfo) && !Objects.equals(commitId, "" ) && commitId != null){
			Commit commit = Commit.deserialize(commitId);
			assert commit != null;
			if(setMap) {
				String treeId = commit.getTree();
				Tree commitTree = Tree.deserialize(treeId);
				assert commitTree != null;
				JitMap.setKeyForMap(tempMap, commitTree, ".", true);
			}
			commitId = commit.getParent();
		}
		return commitId != null && !commitId.equals("");
	}
}